package weibo

import (
	cryRand "crypto/rand" //与 math/rand 包名冲突
	"crypto/rsa"
	"encoding/base64"
	"io/ioutil"
	"math/big"
	"net/http"

	"strings"

	"strconv"

	"net/url"

	"encoding/hex"

	"bufio"
	"io"
	"os"

	"regexp"
	"time"

	"github.com/bitly/go-simplejson"
)

func (p *Processor) Get(_url string) (_data []byte) {
	req, _ := http.NewRequest("GET", _url, nil)
	for k, v := range clientHeader {
		req.Header.Set(k, v)
	}
	resp, _ := p.client.Do(req)
	_data, _ = ioutil.ReadAll(resp.Body)
	defer resp.Body.Close()

	return
}

func (p *Processor) Post(_url string, postBody url.Values) (_data []byte) {
	req, _ := http.NewRequest("POST", _url, strings.NewReader(postBody.Encode()))
	for k, v := range clientHeader {
		req.Header.Set(k, v)
	}
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	resp, _ := p.client.Do(req)
	_data, _ = ioutil.ReadAll(resp.Body)
	defer resp.Body.Close()

	return
}

func (p *Processor) Get_preLogin() (servertime int, nonce, pubkey, rsakv string) {
	username := url.QueryEscape(p.username) // html转义
	username = base64.StdEncoding.EncodeToString([]byte(username))
	urlStr := "https://login.sina.com.cn/sso/prelogin.php?entry=account&callback=sinaSSOController.preloginCallBack&su=" + username + "&rsakt=mod&client=ssologin.js(v1.4.19)&_=" + Now()

	doc := p.Get(urlStr)
	Debug("prelogin.php", string(doc))

	docStr := string(doc)
	begin := strings.Index(docStr, "{")
	end := strings.Index(docStr, "}")
	js, _ := simplejson.NewJson([]byte(docStr[begin:end+1]))

	servertime, _ = js.Get("servertime").Int()
	nonce, _ = js.Get("nonce").String()
	pubkey, _ = js.Get("pubkey").String()
	rsakv, _ = js.Get("rsakv").String()
	Info(servertime, nonce, rsakv)

	return
}

func (p *Processor) Encode(servertime int, nonce, pubkey string) (username, password string) {
	//使用base64对用户名进行编码
	username = url.QueryEscape(p.username) // html转义
	username = base64.StdEncoding.EncodeToString([]byte(username))

	//使用rsa2对password进行编码
	bigInt := new(big.Int)
	bigInt.SetString(pubkey, 16)                 // 将字符串转换成16进制，一般是1024位公钥
	rsaKey := rsa.PublicKey{N: bigInt, E: 65537} // 65537是10001的16进制形式

	codeStr := strconv.Itoa(servertime) + "\t" + nonce + "\n" + p.password
	encryptResult, _ := rsa.EncryptPKCS1v15(cryRand.Reader, &rsaKey, []byte(codeStr))
	password = hex.EncodeToString(encryptResult) //转16进制

	return
}

func (p *Processor) Login(servertime int, nonce, rsakv, pubkey, username, password, door string) {
	postBody := url.Values{
		"entry":       {"account"}, //固定值
		"gateway":     {"1"},       //固定值
		"from":        {},
		"savestate":   {"30"},   //固定值
		"qrcode_flag": {"true"}, //固定值
		"useticket":   {"0"},    //固定值
		"pagerefer":   {},
		"vsnf":        {"1"},      //固定值
		"su":          {username}, // base64加密后的username  rsa公钥加密后的密码
		"service":     {"sso"},    //固定值
		"servertime":  {strconv.Itoa(servertime)},
		"nonce":       {nonce},
		"pwencode":    {"rsa2"}, //固定值
		"rsakv":       {rsakv},
		"sp":          {password},      // rsa公钥加密后的密码
		"sr":          {"1395*822"},    //屏幕分辨率
		"encoding":    {"UTF-8"},       //固定值
		"cdult":       {"3"},           //固定值
		"domain":      {"sina.com.cn"}, //固定值
		"prelt":       {"36"},          //随机构造
		"returntype":  {"TEXT"},        //固定值
	}
	if door != "" {
		postBody.Set("door", door)
	}

	//domain是sina.com，对weibo.com没用
	b := p.Post("http://login.sina.com.cn/sso/login.php?client=ssologin.js(v1.4.15)&_="+Now(), postBody)
	Info("login.php", string(b)) // retcode是 0，代表成功，若失败retcode为非零值

	js, _ := simplejson.NewJson(b)
	if retcode, _ := js.Get("retcode").String();
		retcode == "0" {
		Info("login success")
	} else if retcode == "4049" || retcode == "2070" { //4049  输入验证码  // 2070 验证码错误
		door := p.pin()
		//每次http://login.sina.com.cn/sso/login.php的请求，servertime都要变化，否则报错。
		servertime = servertime + 10
		username, password = p.Encode(servertime, nonce, pubkey) //password跟随servertime也要变
		p.Login(servertime, nonce, rsakv, pubkey, username, password, door)
		return
	} else {
		reason, _ := js.Get("reason").String()
		Fatal("login fail", reason)
	}
	p.uid, _ = js.Get("uid").String()

	//通过跳转，获取weibo.com的cookie
	crossDomainUrlList, _ := js.Get("crossDomainUrlList").StringArray()
	var crossUrl string
	for _, _url := range crossDomainUrlList {
		if strings.Index(_url, "passport.weibo.com") >= 0 {
			crossUrl = _url
			break
		}
	}
	crossUrl += "&callback=sinaSSOController.doCrossDomainCallBack&scriptId=ssoscript0&client=ssologin.js(v1.4.19)&_=" + Now()
	bCross := p.Get(crossUrl)
	//reqCross.Header.Set("Referer", "https://login.sina.com.cn/crossdomain2.php?action=login")
	Debug("passport.weibo.com", string(bCross))

	return
}

func (p *Processor) pin() string {
	pinUrl := "https://login.sina.com.cn/cgi/pin.php?r=" + Now() + "&s=0"
	resp, _ := p.client.Get(pinUrl)
	Debug("pin resp.Cookies", resp.Cookies()) //验证码通过 ULOGIN_IMG=tc-e7ab8a72841201a4bab2aeee2df9ae0c1e63 来配对

	_, err := os.Stat("./img/") //若目录存在，err为nil
	if err != nil {
		os.Mkdir("./img", os.ModePerm)
	}
	dst, _ := os.Create("./img/" + strconv.Itoa(p.index) + ".png")
	io.Copy(dst, resp.Body)
	resp.Body.Close()
	dst.Close() //不close就会在输入pin之前占用图片

	//if p.maxTry > 0 {
	//	_, word := valClient.getWord("./img/" + strconv.Itoa(p.index) + ".png")
	//	p.maxTry = p.maxTry - 1
	//	time.Sleep(2 * time.Second) //百度验证接口不保证qps，一定等2秒。
	//	return word
	//} else {
	println("输入验证码 " + strconv.Itoa(p.index) + ".png : ")
	reader := bufio.NewReader(os.Stdin)
	b, _, _ := reader.ReadLine()

	return string(b)
	//}

}

func (p *Processor) Logout() {
	urlStr1 := "https://login.sina.com.cn/sso/logout.php?entry=account&callback=pluginSSOController.ssoLogoutCallBack&sr=1366*768&client=ssologin.js(v1.4.19)&_=" + Now()
	p.Get(urlStr1)

	urlStr2 := "https://login.sina.com.cn/wbsso/logout?callback=pluginSSOController.doCrossDomainCallBack&scriptId=ssoscript0&client=ssologin.js(v1.4.19)&_=" + Now()
	b := p.Get(urlStr2)
	Debug("logout ", string(b))

	updatePS(p)
}

func updatePS(p *Processor) {
	var txt string
	readLine("./weibo_ps.txt", func(line string) {
		if line != "" {
			txt = txt + line + "\n"
		}
	})

	reg, _ := regexp.Compile(p.username + ".*")
	repl := p.username + "----" + p.password + "----" + time.Now().Format("2006-01-02 15:04:05")
	sss := reg.ReplaceAllString(txt, repl)
	writeFull("./weibo_ps.txt", sss)
}
